<?php

declare( strict_types = 1 );

namespace App\Common\Model\Base\Visitors;

use Hyperf\Database\Commands\ModelData;
use Hyperf\Database\Commands\ModelOption;
use Hyperf\Database\Model\Model;
use Hyperf\Utils\Collection;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use Hyperf\Database\Commands\Ast as nameAst;

class ModelRewriteCreatedAtAndUpdatedAtVisitor extends nameAst\AbstractVisitor {

	/**
	 * @var Model
	 */
	protected $class;

	public function __construct( ModelOption $option, ModelData $data ) {
		parent::__construct( $option, $data );

		$class = $data->getClass();
		$this->class = new $class();
	}

	public function leaveNode( Node $node ) {
		switch( $node ){
			case $node instanceof Node\Stmt\ClassConst:
				switch( $node->consts[0]->name->toLowerString() ){
					case 'created_at':
						if( $this->isRemovedTimestampsOfCreateAt() !== null ){
							return NodeTraverser::REMOVE_NODE;
						}
						break;
					case 'updated_at':
						if( $this->isRemovedTimestampsOfUpdateAt() !== null ){
							return NodeTraverser::REMOVE_NODE;
						}
						break;
				}
				return $node;
		}
	}

	public function afterTraverse( array $nodes ) {
		if( $this->isRemovedTimestampsOfCreateAt() !== true && $this->isRemovedTimestampsOfUpdateAt() !== true ){
			return null;
		}

		foreach( $nodes as $namespace ){
			if( ! $namespace instanceof Node\Stmt\Namespace_ ){
				continue;
			}

			foreach( $namespace->stmts as $class ){
				if( ! $class instanceof Node\Stmt\Class_ ){
					continue;
				}

				foreach( $class->stmts as $key => $node ){
					if( isset( $node->props, $node->props[0], $node->props[0]->name ) && $node->props[0]->name->toLowerString() === 'table' ){
						//
						if( $this->isRemovedTimestampsOfCreateAt() === true ){
							$newNode = $this->rewriteTimestampsOfCreateAt();
							array_splice($class->stmts, $key, 0, [$newNode]);
						}
						//
						if( $this->isRemovedTimestampsOfUpdateAt() === true ){
							$newNode = $this->rewriteTimestampsOfUpdateAt();
							array_splice( $class->stmts, $key, 0, [ $newNode ] );
						}
						return null;
					}
				}
			}
		}
	}

	protected function rewriteTimestampsOfCreateAt( ?Node\Stmt\Const_ $node = null ):?Node\Stmt\Const_ {
		$isRemovedTimestamps = $this->isRemovedTimestampsOfCreateAt();
		if( $isRemovedTimestamps !== true ){
			return null;
		}

		$expr = new Node\Expr\ConstFetch( new Node\Name( 'null' ) );
		if( $node ){
			$node->consts[0]->default = $expr;
		} else{
			$prop = new Node\Const_( 'CREATED_AT', $expr );
			$node = new Node\Stmt\Const_( [ $prop ] );
		}

		return $node;

	}

	protected function rewriteTimestampsOfUpdateAt( ?Node\Stmt\Const_ $node = null ):?Node\Stmt\Const_ {
		$isRemovedTimestamps = $this->isRemovedTimestampsOfUpdateAt();
		if( $isRemovedTimestamps !== true ){
			return null;
		}

		$expr = new Node\Expr\ConstFetch( new Node\Name( 'null' ) );
		if( $node ){
			$node->consts[0]->default = $expr;
		} else{
			$prop = new Node\Const_( 'UPDATED_AT', $expr );
			$node = new Node\Stmt\Const_( [ $prop ] );
		}

		return $node;

	}

	protected function usesTimestampsOfCreateAt():bool {
		$createdAt = $this->class->getCreatedAtColumn();
		$columns = Collection::make( $this->data->getColumns() );

		return $columns->where( 'column_name', $createdAt )->count() > 0;
	}

	protected function usesTimestampsOfUpdateAt():bool {
		$updatedAt = $this->class->getUpdatedAtColumn();
		$columns = Collection::make( $this->data->getColumns() );

		return $columns->where( 'column_name', $updatedAt )->count() > 0;
	}

	protected function isRemovedTimestampsOfCreateAt():?bool {
		$useTimestamps = $this->usesTimestampsOfCreateAt();
		$ref = new \ReflectionClass( get_class( $this->class ) );

		if( ! $ref->getParentClass() ){
			return false;
		}
		//
		$constants = $ref->getConstants();
		if( array_key_exists( 'CREATED_AT', $constants ) ){
			$createdAtValue = $constants['CREATED_AT'];
			if( $createdAtValue === 'created_at' ){
				$createdAtValue = false;
			}
		}else{
			$createdAtValue = false;
		}
		//
		if( $useTimestamps && $createdAtValue === null ){
			return false;
		} elseif( ! $useTimestamps && $createdAtValue !== null ){
			return true;
		}else{
			return null;
		}
	}

	protected function isRemovedTimestampsOfUpdateAt():?bool {
		$useTimestamps = $this->usesTimestampsOfUpdateAt();
		$ref = new \ReflectionClass( get_class( $this->class ) );

		if( ! $ref->getParentClass() ){
			return false;
		}
		//
		$constants = $ref->getConstants();
		if( array_key_exists( 'UPDATED_AT', $constants ) ){
			$updatedAtValue = $constants['UPDATED_AT'];
			if( $updatedAtValue === 'updated_at' ){
				$updatedAtValue = false;
			}
		}else{
			$updatedAtValue = false;
		}
		//
		if( $useTimestamps && $updatedAtValue === null ){
			return false;
		} elseif( ! $useTimestamps && $updatedAtValue !== null ){
			return true;
		}else{
			return null;
		}
/*		if( ! $useTimestamps && $updatedAtValue !== null ){
			return true;
		} elseif( $useTimestamps && $updatedAtValue === null ){
			return false;
		} else{
			return null;
		}*/
	}

}
